package com.renfei.multimediatask.ui.task8;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.RelativeLayout;

import androidx.annotation.RequiresApi;
import androidx.constraintlayout.widget.ConstraintLayout;

import com.renfei.multimediatask.R;
import com.renfei.multimediatask.base.BaseActivity;
import com.renfei.multimediatask.util.ScreenUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.file.WatchEvent;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class VideoDecodecH264Activity extends BaseActivity {

    private static final String TAG = VideoDecodecH264Activity.class.getSimpleName();

    @BindView(R.id.surfaceView)
    SurfaceView surfaceView;
    @BindView(R.id.startDecodec)
    Button startDecodec;
    @BindView(R.id.stopDecodec)
    Button stopDecodec;
    @BindView(R.id.ll_btn_layout)
    RelativeLayout llBtnLayout;
    private String decodeFilePath;


    private SurfaceHolder surfaceHolder;

    private DecodeH264Thread decodeH264Thread;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_video_decodec_h264;
    }

    @Override
    protected void initListener() {
        decodeFilePath = getParentFilePath() + "video_encodec.h264";


        int width = ScreenUtils.getScreentWidth(this);
//        width/640=height/480;

        float scale = (float) (640.0 / 480);
        int height = (int) (width / scale);


        ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
                width,
                height
        );
        surfaceView.setLayoutParams(params);

        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                loge(TAG, "surfaceCreated");
                decodeH264Thread = new DecodeH264Thread(new WeakReference<>(VideoDecodecH264Activity.this));
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                loge(TAG, "surfaceChanged");
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                loge(TAG, "surfaceDestroyed");
                if (null != decodeH264Thread) {
                    decodeH264Thread.stopThread();
                }
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (null != decodeH264Thread) {
            decodeH264Thread.stopThread();

        }

    }

    @OnClick({R.id.startDecodec, R.id.stopDecodec})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.startDecodec:
                if (null != decodeH264Thread) {
                    decodeH264Thread.start();
                }
                break;
            case R.id.stopDecodec:
                break;
        }
    }


    static class DecodeH264Thread extends Thread {
        //等待队列超时时间
        private int TIMEOUT_USEC = 12000;

        private static final String TAG = DecodeH264Thread.class.getSimpleName();
        WeakReference<VideoDecodecH264Activity> weakReference;

        private MediaCodec videoDecodec;
        private MediaFormat videoFormat;

        //        640x480
        private int videoWidth = 640;
        private int videoHeight = 480;

        //一般H264帧大小不超过200k,如果解码失败可以尝试增大这个值
        private static int FRAME_MAX_LEN = 300 * 1024;

        //是否正在解码
//        private boolean isDecodec = false;

        //文件读取完成标志
        private boolean isFileReadFinish = false;

        public DecodeH264Thread(WeakReference<VideoDecodecH264Activity> weakReference) {
            this.weakReference = weakReference;
            initDecodec();
        }

        private void initDecodec() {
            weakReference.get().loge(TAG, "initDecodec");
            try {

                videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, videoWidth, videoHeight);

                videoDecodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

                videoDecodec.configure(videoFormat,
                        weakReference.get().surfaceHolder.getSurface(),
                        null, 0
                );
                videoDecodec.start();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void run() {
            super.run();
            FileInputStream fileInputStream;

            File file = new File(weakReference.get().decodeFilePath);
            if (!file.exists()) {
                Log.e(TAG, weakReference.get().decodeFilePath + " : 文件不存在");
                return;
            }

            try {
                fileInputStream = new FileInputStream(file);
                byte[] readData = new byte[10 * 1024];
                while (!isFileReadFinish) {
                    if (fileInputStream.available() > 0) {
                        int readLen = fileInputStream.read(readData);
                        if (readLen >= 0) {
                            deCodecData(readData, readLen, false);
                        } else {
                            deCodecData(readData, readLen, true);
                        }
                    } else {
                        //文件读取结束
                        isFileReadFinish = true;
                    }
                }
                //文件读取结束
                isFileReadFinish = true;

                videoDecodec.stop();
                videoDecodec.release();
                Log.e(TAG, "解码完成: ");

                fileInputStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }


            //解码完成
        }

        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void deCodecData(byte[] readData, int readLen, boolean isEndOes) {
            weakReference.get().loge(TAG, "deCodecData   " + readLen);
            int inputBufferIndex = videoDecodec.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = videoDecodec.getInputBuffer(inputBufferIndex);
                inputBuffer.clear();
                inputBuffer.put(readData);
                if (isEndOes) {
                    videoDecodec.queueInputBuffer(inputBufferIndex, 0, 0, 0L,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                } else {
                    videoDecodec.queueInputBuffer(inputBufferIndex, 0, readLen, getPTSUs(), 0);
                }
            }
            //处理输出
            MediaCodec.BufferInfo videoBufferinfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = videoDecodec.dequeueOutputBuffer(videoBufferinfo, TIMEOUT_USEC);

            if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                Log.e(TAG, "MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: ");
            } else {
                while (outputBufferIndex > 0) {
                    ByteBuffer outputBuffer = videoDecodec.getOutputBuffer(outputBufferIndex);

                    videoDecodec.releaseOutputBuffer(outputBufferIndex, true);
                    outputBufferIndex = videoDecodec.dequeueOutputBuffer(videoBufferinfo, TIMEOUT_USEC);
                }
            }

        }


        //手动终止读取文件，结束线程
        public void stopThread() {
            isFileReadFinish = true;
        }




        /**
         * previous presentationTimeUs for writing
         */
        private long prevOutputPTSUs = 0;

        /**
         * get next encoding presentationTimeUs
         *
         * @return
         */
        protected long getPTSUs() {
            long result = System.nanoTime() / 1000L;
            // presentationTimeUs should be monotonic
            // otherwise muxer fail to write
            if (result < prevOutputPTSUs)
                result = (prevOutputPTSUs - result) + result;
            return result;
        }

    }
}
